
 
#include <unistd.h>
#include <string.h>
#include "qemu/osdep.h"
#include "qemu/module.h"
#include "hw/i2c/i2c.h"
#include "migration/vmstate.h"
#include "hw/qdev-properties.h"
#include "hw/qdev-properties-system.h"
#include "qapi/visitor.h"
#include "trace.h"
#include "chardev/char-fe.h"

#define TYPE_GENERIC_I2CSLAVE "generic-i2c-slave"
#define GENERIC_I2CSLAVE(obj) OBJECT_CHECK(slave_state, (obj), TYPE_GENERIC_I2CSLAVE)

#ifdef DEBUG_GENERIC_I2C
#define DPRINTF(_x ...) printf(_x)
#else
#define DPRINTF(_x ...)
#endif

#define GENERNIC_I2C_FRAME_LEN       2
#define GENERNIC_I2C_SLEEP_TIME      100000
#define GENERNIC_I2C_BYTE_LEN        8

typedef struct {
    I2CSlave parent_obj;
    CharBackend chrbe;
    uint32_t reg_width;
    uint32_t reg_bytes;
    uint32_t reg_temp;
    bool address_byte;
    bool transfer_start;
    bool transfer_recv;
    uint8_t address;
    uint8_t reg_addr;
    uint32_t reg_value;
    uint32_t reg_mask;
    uint32_t reg[0x100];
    uint32_t mask[0x100];
} slave_state;

static const VMStateDescription vmstate_generic_i2cslave = {
    .name = TYPE_GENERIC_I2CSLAVE,
    .version_id = 1,
    .minimum_version_id = 1,
    .fields = (VMStateField[]) {
        VMSTATE_I2C_SLAVE(parent_obj, slave_state),
        VMSTATE_UINT32(reg_width, slave_state),
        VMSTATE_UINT32(reg_bytes, slave_state),
        VMSTATE_UINT32(reg_temp, slave_state),
        VMSTATE_BOOL(address_byte, slave_state),
        VMSTATE_BOOL(transfer_start, slave_state),
        VMSTATE_BOOL(transfer_recv, slave_state),
        VMSTATE_UINT8(address, slave_state),
        VMSTATE_UINT8(reg_addr, slave_state),
        VMSTATE_UINT32(reg_value, slave_state),
        VMSTATE_UINT32(reg_mask, slave_state),
        VMSTATE_UINT32_ARRAY(reg, slave_state, 0x100),
        VMSTATE_UINT32_ARRAY(mask, slave_state, 0x100),
        VMSTATE_END_OF_LIST()
    }
};

static int generic_i2cslave_event(I2CSlave *i2c, enum i2c_event event)
{
    slave_state *s = GENERIC_I2CSLAVE(i2c);

    if (qemu_chr_fe_backend_connected(&s->chrbe)) {
        if (qemu_chr_fe_backend_open(&s->chrbe)) {
            const uint8_t buf[2] = {0xAA, (uint8_t)event};
            qemu_chr_fe_write_all(&s->chrbe, buf, sizeof(buf));
            if (event == I2C_START_SEND) {
                const uint8_t tmp[2] = {0xF0, s->parent_obj.address << 1};
                qemu_chr_fe_write_all(&s->chrbe, tmp, sizeof(tmp));
            } else if (event == I2C_START_RECV) {
                const uint8_t tmp[2] = {0xF0, (s->parent_obj.address << 1) + 1};
                qemu_chr_fe_write_all(&s->chrbe, tmp, sizeof(tmp));
            }
            // imcu为脉冲中断触发，所以需要增加延时效果
            usleep(GENERNIC_I2C_SLEEP_TIME);
        }
        return 0;
    }

    switch (event) {
        case I2C_START_SEND:
            s->transfer_start = true;
            s->transfer_recv = true;
            s->address_byte = true;
            s->reg_bytes = 0;
            break;
        case I2C_START_RECV:
            s->transfer_start = true;
            s->transfer_recv = false;
            s->address_byte = false;
            s->reg_bytes = 0;
            break;
        case I2C_FINISH:
            s->transfer_start = false;
            break;
        case I2C_NACK:
            break;
        default:
            break;
    }

    return 0;
}

static uint8_t generic_i2cslave_rx(I2CSlave *i2c)
{
    slave_state *s = GENERIC_I2CSLAVE(i2c);

    if (qemu_chr_fe_backend_connected(&s->chrbe)) {
        if (qemu_chr_fe_backend_open(&s->chrbe)) {
            uint8_t buf[2] = {0xA5, 0x01};
            qemu_chr_fe_write_all(&s->chrbe, buf, sizeof(buf));
            // imcu为脉冲中断触发，所以需要增加延时效果
            usleep(GENERNIC_I2C_SLEEP_TIME);
            qemu_chr_fe_read_all(&s->chrbe, buf, sizeof(buf));
            if (buf[0] == 0x5A) {
                return buf[1];
            }
        }
    } else if (s->transfer_start && !s->transfer_recv) {
        const uint32_t width = s->reg_width > 4 ? 4 : (s->reg_width == 0 ? 1 : s->reg_width);
        const uint8_t rx = s->reg[s->address] >> ((width - 1 - s->reg_bytes) << 3);
        if (++s->reg_bytes >= width) {
            s->reg_bytes = 0;
            s->address++;
        }
        return rx;
    }

    return 0;
}

static int generic_i2cslave_tx(I2CSlave *i2c, uint8_t data)
{
    slave_state *s = GENERIC_I2CSLAVE(i2c);

    if (qemu_chr_fe_backend_connected(&s->chrbe)) {
        if (qemu_chr_fe_backend_open(&s->chrbe)) {
            const uint8_t buf[2] = {0x55, data};
            qemu_chr_fe_write_all(&s->chrbe, buf, sizeof(buf));
            // imcu为脉冲中断触发，所以需要增加延时效果
            usleep(GENERNIC_I2C_SLEEP_TIME);
        }
    } else if (s->transfer_start && s->transfer_recv) {
        if (s->address_byte) {
            s->address = data;
            s->address_byte = false;
        } else {
            const uint32_t width = s->reg_width >= 4 ? 4 : (s->reg_width == 0 ? 1 : s->reg_width);
            s->reg_temp = s->reg_temp << GENERNIC_I2C_BYTE_LEN | data;
            if (++s->reg_bytes >= width) {
                s->reg[s->address] = (s->reg[s->address] & ~s->mask[s->address]) | (s->reg_temp & s->mask[s->address]);
                s->address++;
                s->reg_bytes = 0;
            }
        }
    }

    return 0;
}

static void generic_i2cslave_reset(DeviceState *dev)
{
    slave_state *s = GENERIC_I2CSLAVE(dev);
    s->transfer_start = false;
}

static int generic_i2cslave_backend_can_read(void *opaque)
{
    return GENERNIC_I2C_FRAME_LEN;
}

static void generic_i2cslave_backend_read(void *opaque, const uint8_t *buf, int size)
{
    // 收到host发送的数据打印一下
    DPRINTF("generic_i2cslave_backend recv 0x%x bytes: ", size);
    for (int i = 0; i < size; i++) {
        DPRINTF("0x%02x ", buf[i]);
    }
    DPRINTF("\n");
}

static void generic_i2cslave_backend_event_handle(void *opaque, QEMUChrEvent event)
{
    if (event == CHR_EVENT_OPENED) {
        DPRINTF("generic_i2cslave_backend is connetcting\n");
    }
}

static void generic_i2cslave_realize(DeviceState *dev, Error **errp)
{
    slave_state *s = GENERIC_I2CSLAVE(dev);
    if (qemu_chr_fe_backend_connected(&s->chrbe)) {
        qemu_chr_fe_set_handlers(&s->chrbe,
                                 generic_i2cslave_backend_can_read,
                                 generic_i2cslave_backend_read,
                                 generic_i2cslave_backend_event_handle,
                                 NULL,
                                 s,
                                 NULL,
                                 true);
    }
}

static void generic_i2cslave_access_addr(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp)
{
    uint8_t *ptr = (uint8_t *)object_field_prop_ptr(obj, (Property *)opaque);
    visit_type_uint8(v, name, ptr, errp);
}

static const PropertyInfo generic_i2cslave_prop_regaddr = {
    .name  = "uint8",
    .get   = generic_i2cslave_access_addr,
    .set   = generic_i2cslave_access_addr,
    // .realized_set_allowed = true,
};

static void generic_i2cslave_get_reg(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp)
{
    slave_state *s = GENERIC_I2CSLAVE(obj);
    s->reg_value = s->reg[s->reg_addr];
    uint32_t *ptr = (uint32_t *)object_field_prop_ptr(obj, (Property *)opaque);
    visit_type_uint32(v, name, ptr, errp);
}

static void generic_i2cslave_set_reg(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp)
{
    slave_state *s = GENERIC_I2CSLAVE(obj);
    uint32_t *ptr = (uint32_t *)object_field_prop_ptr(obj, (Property *)opaque);
    visit_type_uint32(v, name, ptr, errp);
    s->reg[s->reg_addr] = s->reg_value;
}

static const PropertyInfo generic_i2cslave_prop_regvalue = {
    .name  = "uint32",
    .get   = generic_i2cslave_get_reg,
    .set   = generic_i2cslave_set_reg,
    // .realized_set_allowed = true,
};

static void generic_i2cslave_get_mask(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp)
{
    slave_state *s = GENERIC_I2CSLAVE(obj);
    s->reg_mask = s->mask[s->reg_addr];
    uint32_t *ptr = (uint32_t *)object_field_prop_ptr(obj, (Property *)opaque);
    visit_type_uint32(v, name, ptr, errp);
}

static void generic_i2cslave_set_mask(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp)
{
    slave_state *s = GENERIC_I2CSLAVE(obj);
    uint32_t *ptr = (uint32_t *)object_field_prop_ptr(obj, (Property *)opaque);
    visit_type_uint32(v, name, ptr, errp);
    s->mask[s->reg_addr] = s->reg_mask;
}

static const PropertyInfo generic_i2cslave_prop_regmask = {
    .name  = "uint32",
    .get   = generic_i2cslave_get_mask,
    .set   = generic_i2cslave_set_mask,
};

static Property generic_i2cslave_properties[] = {
    DEFINE_PROP_CHR("chardev", slave_state, chrbe),
    DEFINE_PROP_UINT32("reg-width", slave_state, reg_width, 1),
    DEFINE_PROP_UNSIGNED_NODEFAULT("reg-addr", slave_state, reg_addr, generic_i2cslave_prop_regaddr, uint8_t),
    DEFINE_PROP_UNSIGNED_NODEFAULT("reg-value", slave_state, reg_value, generic_i2cslave_prop_regvalue, uint32_t),
    DEFINE_PROP_UNSIGNED_NODEFAULT("reg-mask", slave_state, reg_mask, generic_i2cslave_prop_regmask, uint32_t),
    DEFINE_PROP_END_OF_LIST(),
};

static void generic_i2cslave_class_init(ObjectClass *klass, void *data)
{
    DeviceClass *dc = DEVICE_CLASS(klass);
    I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);

    k->event = generic_i2cslave_event;
    k->recv = generic_i2cslave_rx;
    k->send = generic_i2cslave_tx;
    dc->reset = generic_i2cslave_reset;
    dc->realize = generic_i2cslave_realize;
    dc->vmsd = &vmstate_generic_i2cslave;
    device_class_set_props(dc, generic_i2cslave_properties);
}

static void generic_i2cslave_init(Object *obj)
{
    slave_state *s = GENERIC_I2CSLAVE(obj);
    memset(s->mask, 0xff, sizeof(s->mask));
}

static const TypeInfo generic_i2cslave_info = {
    .name          = TYPE_GENERIC_I2CSLAVE,
    .parent        = TYPE_I2C_SLAVE,
    .instance_size = sizeof(slave_state),
    .class_init    = generic_i2cslave_class_init,
    .instance_init = generic_i2cslave_init,
};

static void generic_i2cslave_register_types(void)
{
    type_register_static(&generic_i2cslave_info);
}

type_init(generic_i2cslave_register_types)